/* 
 * Copyright (c) 2012 Christoph Mueller <ruunhb@googlemail.com>
 * 
 * Lqstal is free software: you can redistribute it and/or modify
 * it under the terms of the Lesser GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Lqstal is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * Lesser GNU General Public License for more details.
 *
 * You should have received a copy of the Lesser GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "array.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h>

struct LqArray
{
	uint8_t* data;
	size_t alloc;
	size_t length;
        size_t size;
};




struct LqArray*        
lq_array_new(size_t init_capacity, size_t size)
{
	assert(init_capacity != 0);

	struct LqArray* array = malloc(sizeof(struct LqArray));
	array->alloc = init_capacity;
	array->length = 0;
        array->size = size;
	array->data = calloc(size, array->alloc);

	return array;
}



void                    
lq_array_free(struct LqArray* array)
{
	assert(array != 0);

	free(array->data);
	free(array);
}



size_t                  
lq_array_size(struct LqArray* array)
{
	assert(array != 0);

	return array->length;
}



size_t                  
lq_array_capacity(struct LqArray* array)
{
	assert(array != 0);

	return array->alloc;
}


size_t                  
lq_array_ensure(struct LqArray* array, size_t min_capacity)
{
	assert(array != 0);

	if(array->alloc < min_capacity) {
		size_t new_capacity = (array->alloc >> 1);
		array->alloc = (new_capacity > min_capacity) ? new_capacity : min_capacity;
		array->data = realloc(array->data, array->alloc * array->size);
	}

	return array->alloc;
}


size_t                  
lq_array_trim(struct LqArray* array)
{
	assert(array != 0);

	array->data = realloc(array->data, array->length * array->size);
	array->alloc = array->length;

	return array->alloc;
}


void                    
lq_array_append(struct LqArray* array, const_pointer data, size_t elements)
{
	assert(array != 0);

	lq_array_ensure(array, array->length + elements);

	memcpy(array->data + array->length * array->size, data, elements * array->size);

	array->length += elements;
}


int
lq_array_insert(struct LqArray* array, size_t index, const_pointer data, size_t elements)
{
	assert(array != 0);

        if(index >= array->length)
                return FALSE;

	lq_array_ensure(array, array->length + elements);

	memmove(array->data + (index + elements) * array->size, 
                        array->data + index * array->size, 
                        (array->length - index) * array->size);

        memcpy(array->data + index * array->size, data, elements * array->size);

	array->length += elements;

	return TRUE;
}


int		
lq_array_replace(struct LqArray* array, size_t index, const_pointer data, size_t elements)
{
        assert(array != 0);

        if(index >= array->length && index + elements > array->length)
                return FALSE;

        lq_array_ensure(array, array->length + elements);

        memcpy(array->data + index * array->size, data, elements * array->size);

        return TRUE;
}

int
lq_array_remove(struct LqArray* array, size_t index, size_t elements)
{
	assert(array != 0);

	if(index >= array->length)
		return FALSE;

	memmove(array->data + index * array->size, 
                        array->data + (index + elements) * array->size, 
                        (array->length - index - elements) * array->size);

	array->length -= elements;

	return TRUE;
}



void
lq_array_clear(struct LqArray* array)
{
        assert(array != 0);

        array->length = 0;
}



pointer                 
lq_array_get(struct LqArray* array, size_t index)
{
	assert(array != 0);

	if(index < array->length)
		return array->data + index * array->size;

	return 0;
}



pointer                 
lq_array_last(struct LqArray* array)
{
	assert(array != 0);

	if(array->length > 0)
		return array->data + (array->length - 1) * array->size;

	return 0;
}



pointer
lq_array_clone(struct LqArray* array)
{
	assert(array != 0);

	uint8_t* ptr = 0;

	if(array->length > 0) {
		ptr = calloc(array->size, array->length);
		memcpy(ptr, array->data, array->length * array->size);
	}
	
	return ptr;
}


pointer
lq_array_pointer(struct LqArray* array)
{
        assert(array != 0);

        return array->data;
}


struct LqPtrArray
{
	pointer* data;
	size_t alloc;
	size_t length;
};


struct LqPtrArray*     
lq_ptr_array_new(size_t init_capacity)
{
	assert(init_capacity != 0);

	struct LqPtrArray* array = malloc(sizeof(struct LqPtrArray));
	array->alloc = init_capacity;
	array->length = 0;
	array->data = calloc(sizeof(pointer), array->alloc);
	
	return array;
}


void                    
lq_ptr_array_free(struct LqPtrArray* array)
{
	assert(array != 0);

	free(array->data);
	free(array);
}


size_t                  
lq_ptr_array_size(struct LqPtrArray* array)
{
	assert(array != 0);

	return array->length;
}


size_t                  
lq_ptr_array_capacity(struct LqPtrArray* array)
{
	assert(array != 0);

	return array->alloc;
}


size_t                  
lq_ptr_array_ensure(struct LqPtrArray* array, size_t min_capacity)
{
	assert(array != 0);

	if(array->alloc < min_capacity) {
		size_t new_capacity = (array->alloc >> 1);
		array->alloc = (new_capacity > min_capacity) ? new_capacity : min_capacity;
		array->data = realloc(array->data, array->alloc * sizeof(pointer));
	}

	return array->alloc;
}


size_t                  
lq_ptr_array_trim(struct LqPtrArray* array)
{
	assert(array != 0);

	array->data = realloc(array->data, array->length * sizeof(pointer));
	array->alloc = array->length;

	return array->alloc;
}


void                    
lq_ptr_array_append(struct LqPtrArray* array, pointer ptr)
{
	assert(array != 0);

	lq_ptr_array_ensure(array, array->length + 1); 

	*(array->data + array->length) = ptr;

	array->length += 1;
}


int             
lq_ptr_array_insert(struct LqPtrArray* array, size_t index, pointer ptr)
{
	assert(array != 0);

	if(index >= array->length)
		return FALSE;

	lq_ptr_array_ensure(array, array->length + 1); 

	memmove(array->data + index + 1, array->data + index, (array->length - index) * sizeof(pointer));

	*(array->data + index) = ptr;

	array->length += 1;

	return TRUE;
}


pointer			
lq_ptr_array_replace(struct LqPtrArray* array, size_t index, pointer ptr)
{
        assert(array != 0);

        if(index >= array->length)
                return 0;

        pointer data = *(array->data + index);

        *(array->data + index) = ptr;

        return data;
}

pointer                
lq_ptr_array_remove(struct LqPtrArray* array, size_t index)
{
	assert(array != 0);

	if(index >= array->length)
		return 0;

	pointer data = *(array->data + index);

	memmove(array->data + index, array->data + index + 1, (array->length - index - 1) * sizeof(pointer));

	array->length -= 1;

	return data;
}


pointer                 
lq_ptr_array_get(struct LqPtrArray* array, size_t index)
{
	assert(array != 0);

	if(index < array->length)
		return *(array->data + index);

	return 0;
}

void			
lq_ptr_array_clear(struct LqPtrArray* array, lq_free_funptr fun)
{
        assert(array != 0);

        if(fun != 0) {
                size_t index = 0;
                for(index = 0; index < array->length; ++index)
                        fun(*(array->data + index));
        }

        array->length = 0;
}


